use std::{borrow::Borrow, fmt::Debug};

use serde::Serialize;
use serde_json::{json, Value};
use tracing::error;

use crate::error::BizError;

#[derive(Debug, Serialize)]
pub struct BizResponse<T: Serialize> {
    code: String,
    msg: String,
    data: Option<T>,
    err_data: Option<Value>,
}

impl<T> BizResponse<T>
where
    T: Serialize + Clone,
{
    pub fn success(data: T) -> Self {
        Self {
            code: "200".to_string(),
            msg: "OK".to_string(),
            data: Some(data),
            err_data: None,
        }
    }

    pub fn fail(code: &str, msg: &str) -> Self {
        Self::fail_data(code, msg, None)
    }

    pub fn fail_data<E>(code: &str, msg: &str, err_data: E) -> Self
    where
        E: Borrow<Option<Value>>,
    {
        Self {
            code: code.to_string(),
            msg: msg.to_string(),
            data: None,
            err_data: err_data.borrow().clone(),
        }
    }

    pub fn fail_with_biz_error(err: BizError, msg: Option<&str>) -> Self {
        Self::fail_with_biz_error_ref(&err, msg)
    }

    pub fn fail_with_biz_error_ref(err: &BizError, msg: Option<&str>) -> Self {
        let msg = match msg {
            Some(s) => s.to_string(),
            None => err.to_string(),
        };

        let code = format!("{:?}", err);
        // 如果错枚举带括号, 则取括号前面的值
        let code = if let Some(_index) = code.find("(") {
            &code[.._index]
        } else {
            &code
        };

        let err_opt = match err {
            BizError::InvalidParams(errs) => errs,
            _ => &None,
        };

        Self::fail_data(code, msg.as_str(), err_opt)
    }

    pub fn to_json_value(&self) -> Value {
        serde_json::to_value(self).unwrap()
    }
}

#[derive(Debug, Serialize)]
pub struct PageResult<T: Serialize> {
    pub page_num: u64,
    pub page_size: u64,
    pub total: u64,
    pub items: Vec<T>,
}

impl<T: Serialize> PageResult<T> {
    pub fn new(page_num: u64, page_size: u64, total: u64, items: Vec<T>) -> Self {
        Self {
            page_num,
            page_size,
            total,
            items,
        }
    }
}

impl<T> Into<Value> for PageResult<T>
where
    T: Serialize,
{
    fn into(self) -> Value {
        serde_json::to_value(self).unwrap()
    }
}

pub trait PageResultTryFrom<S, T>
where
    S: Serialize,
    T: Serialize + TryFrom<S>,
{
    fn try_build_from(src: PageResult<S>) -> anyhow::Result<PageResult<T>>;
}

impl<S, T> PageResultTryFrom<S, T> for PageResult<T>
where
    S: Serialize,
    T: Serialize + TryFrom<S>,
{
    fn try_build_from(src: PageResult<S>) -> anyhow::Result<PageResult<T>> {
        let PageResult {
            page_num,
            page_size,
            total,
            items,
        } = src;

        let mut _items: Vec<T> = vec![];
        for item in items {
            let _item = match T::try_from(item) {
                Err(_) => {
                    anyhow::bail!("数据转换异常")
                }
                Ok(item) => item,
            };
            _items.push(_item);
        }

        Ok(PageResult {
            page_num,
            page_size,
            total,
            items: _items,
        })
    }
}
